> ## Documentation Index
> Fetch the complete documentation index at: https://sequence-0fb8d9e6-api_docs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Sequence Analytics API in Dune

> Learn how to use the Sequence Builder analytics feature to query user data for your project with a serverless Cloudflare Worker.

Time to complete: 20-30 minutes

In this guide, we will show you how to use the [Sequence Builder](https://sequence.build/) analytics feature to query information about usage from your users for your specific project leveraging a serverless [Cloudflare Worker](https://www.cloudflare.com/).

Showcase to your community how well you're doing via a [Dune](https://dune.com/) dashboard to give a sense of connectiveness, or, use the produced API to incorporate intelligent feedback loops into your game driven by user analytics.

You can view an example of the output for this guide [here](https://dune.com/mmhorizon/dungeon-minter-analytics)

1. Access Key Management: Claim a secret access key to interact with the Sequence stack
2. Cloudflare Worker: Create a function that queries the Sequence stack and produces project specific data points
3. Dune Dashboard: Create a view into the data as a shareable dashboard

<Note>
  A reference to the template code can be found
  [here](https://github.com/0xsequence-demos/template-cloudflare-worker-wallets-analytics)
</Note>

## 1. Access Key Management

You'll need to acquire a Secret Access Key for your project in order to authenticate your application with the Sequence stack. Use the following steps:

### Secret Access Key Creation

<Steps>
  <Step title="Access Settings">
    First start by accessing settings, and selecting the API Keys card:

    <Frame>
      <img src="https://mintcdn.com/sequence-0fb8d9e6-api_docs/csTTQfNeitUEy5bt/images/builder/builder_settings_access_keys.png?fit=max&auto=format&n=csTTQfNeitUEy5bt&q=85&s=eaae0455a219b071338925e5c9b316d2" alt="builder settings access keys" width="1757" height="771" data-path="images/builder/builder_settings_access_keys.png" />
    </Frame>
  </Step>

  <Step title="Add Service Account">
    Scroll down and select `+ Add Service Account`:

    <Frame>
      <img src="https://mintcdn.com/sequence-0fb8d9e6-api_docs/csTTQfNeitUEy5bt/images/builder/builder_settings_add_service_account.png?fit=max&auto=format&n=csTTQfNeitUEy5bt&q=85&s=265e6ece1824e871d98a1175df7fa070" alt="builder settings add service account" width="2033" height="805" data-path="images/builder/builder_settings_add_service_account.png" />
    </Frame>
  </Step>

  <Step title="Select Write Permission">
    Then change the permission to `Write`, click on `+ Add Service Account`, and select `Confirm`:

    <Frame>
      <img src="https://mintcdn.com/sequence-0fb8d9e6-api_docs/csTTQfNeitUEy5bt/images/builder/builder_settings_add_service_account_confirm.png?fit=max&auto=format&n=csTTQfNeitUEy5bt&q=85&s=0d43e1b5a7bfd3f0f8d686dde0ac1635" alt="builder settings add service account" width="2033" height="771" data-path="images/builder/builder_settings_add_service_account_confirm.png" />
    </Frame>

    Finally `copy` the key and store it in a safe location, as you will not have access to this in the future from the Sequence Builder.
  </Step>
</Steps>

## 2. Cloudflare Worker

In this example, we utilize a Cloudflare Worker to experience automatic scaling based on dashboard use, and easy deployments from the cli, but of course you can use your own backend or other serverless alternatives.

<Steps>
  <Step title="Create Project">
    In order to create the project from scratch, first create a project with `mkdir`, `cd` into the project, and run `pnpm init` to create a `package.json`.
  </Step>

  <Step title="'Hello World' Worker">
    Make sure wrangler cli is installed in your project and set the `wrangler` keyword as an alias in your local bash session.

    ```shell theme={null}
    pnpm install wrangler --save-dev
    alias wrangler='./node_modules/.bin/wrangler'
    ```

    Create an account on the [Cloudflare site](https://cloudflare.com/) and perform a login step to login to your Cloudflare dashboard to connect the Cloudflare platform to your local development environment.

    ```shell theme={null}
    wrangler login
    ```

    Once logged in, initialize the project in the directory with the command `wrangler init` and accept one of the randomly generated project folder names provided that you like, and follow the prompts to initialize your git tracked typescript `"Hello World" Worker` application.

    ```shell theme={null}
    wrangler init
    ```

    To complete this step, you should press enter 4 times after `wrangler init` with the last 2 step answered as `No` to decline git versioning and deployment.

    This will clone down a starter repository that can be used to deploy code to the cloud.

    <Note>
      Local API Testing <br />
      At any point in the guide, you can use the `wrangler dev` command in the project folder for
      local testing
    </Note>

    #### Deploy Test

    Finally, `cd` into the randomly generated project folder, and perform a `wrangler deploy` command.

    This should print a URL, which you can enter in the browser the URL `https://<app>.<account>.workers.dev` to view the `Hello World!` result.
  </Step>

  <Step title="Setup Config, Routes, & Mock functions">
    Once you have the project setup, update your `wrangler.toml` with the following variables, where `DAYS` is the time period you're looking at:

    ```shell theme={null}
    [vars]
    SECRET_API_ACCESS_KEY = "<SECRET_API_ACCESS_KEY>"
    PROJECT_ID = <PROJECT_ID>
    DAYS = <DAYS>
    ```

    Then include the `Env` type with the variables in `index.ts`:

    ```ts theme={null}
    export interface Env {
      PROJECT_ID: number;
      SECRET_API_ACCESS_KEY: string;
      DAYS: number;
    }
    ```

    Replace the existing `fetch` function with the following mocked function calls:

    ```ts theme={null}
    export default {
      async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
        const url = new URL(request.url);

        // Handle different endpoints
        if (url.pathname === "/dailyActiveUsers") {
          return handleDailyWallets(env, request);
        } else if (url.pathname === "/totalTransactionsSent") {
          return handleTotalTxns(env, request);
        } else {
          return new Response("No function for this URL", {status: 405});
        }
      }
    };
    ```

    Using the following functions:

    ```ts theme={null}
    const handleDailyWallets = async (env: Env, request: Request) => {
      return new Response(JSON.stringify({endpoint: "daily"}), {status: 200});
    };

    const handleTotalTxns = async (env: Env, request: Request) => {
      return new Response(JSON.stringify({endpoint: "total"}), {status: 200});
    };
    ```
  </Step>

  <Step title="Date Formatting">
    Next, include the following utility functions to parse the correct date from the updated value in the `wrangler.toml` for the `DAYS` variable:

    ```ts theme={null}
    const endDate = () => {
      const today = new Date();
      return today.toISOString().substring(0, 10); // only including the YYYY-MM-DD date
    };

    const startDate = (env: Env) => {
      const today = new Date();

      // Format today's date as a string
      const daysBefore = new Date(today);
      daysBefore.setDate(daysBefore.getDate() - env.DAYS);

      // Format the date 7 days before as a string by only including the YYYY-MM-DD date
      const daysBeforeString = daysBefore.toISOString().substring(0, 10);
      return daysBeforeString;
    };
    ```
  </Step>

  <Step title="Daily Active Users">
    Now, handle the `Daily Active Users` request by using the following function, that calls the Sequence Analytics API:

    ```ts theme={null}
    const handleDailyWallets = async (env: Env, request: Request) => {
      const resp = await fetch(`https://api.sequence.build/rpc/Analytics/WalletsDaily`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${env.SECRET_API_ACCESS_KEY}`
        },
        body: JSON.stringify({
          filter: {
            dateInterval: "DAY",
            endDate: endDate(),
            projectId: env.PROJECT_ID,
            startDate: startDate(env)
          }
        })
      });

      const data: any = await resp.json();
      return new Response(JSON.stringify(data.walletStats), {status: 200});
    };
    ```
  </Step>

  <Step title="Total Transactions Sent">
    Finally, add the following function for the `Total Transactions Sent`:

    ```ts theme={null}
    const handleTotalTxns = async (env: Env, request: Request) => {
      const resp = await fetch(
        `https://api.sequence.build/rpc/Analytics/WalletsTxnSentTotal`,
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            Authorization: `Bearer ${env.SECRET_API_ACCESS_KEY}`
          },
          body: JSON.stringify({
            filter: {
              dateInterval: "DAY",
              endDate: endDate(),
              projectId: env.PROJECT_ID,
              startDate: startDate(env)
            }
          })
        }
      );

      const data: any = await resp.json();
      return new Response(JSON.stringify(data.walletStats), {status: 200});
    };
    ```
  </Step>

  <Step title="Included Spacing for Days of Zero Data">
    Included in the Sequence Analytics API is the days with zero activity removed from the response. However, if you want to include days with zero data in your Dune queries to showcase the spacing of time in relation of data, you can use the following function to complete the days not showing data in the correct date format:

    ```typescript theme={null}
    const fillMissingDates = (data: any[], startDate: string, endDate: string) => {
      const filledData: {value: number; label: string}[] = [];
      const start = new Date(startDate);
      const end = new Date(endDate);

      for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
        const dateString = d.toISOString().substring(0, 10);
        const existingData = data.find((entry) => entry.label === dateString);
        if (existingData) {
          filledData.push(existingData);
        } else {
          filledData.push({value: 0, label: dateString});
        }
      }

      return filledData;
    };
    ```

    Then for **both** call responses include the following method call with the `walletStats` data passed in:

    ```typescript theme={null}
    	...
    	const data: any = await resp.json();
    	const filledData = fillMissingDates(data.walletStats, startDate(env), endDate());
    	return new Response(JSON.stringify(filledData), { status: 200 });
    }
    ```
  </Step>
</Steps>

Now you can test your API by calling the respective paths with host name (`/dailyActiveUsers` & `/totalTransactionsSent`) once you have redeployed with `wrangler deploy`.

<Note>
  For more examples of endpoints available through the Analytics API, please check out the
  [overview](/api-references/analytics/overview) page.
</Note>

## 3. Dune Dashboard

<Steps>
  <Step title="Dune Sign Up">
    First, sign up to [Dune](https://dune.com/)
  </Step>

  <Step title="Create Query">
    Access your account at `https://dune.com/<account>` and select the `Create` button and `New query`.

    <Frame>
      <img src="https://mintcdn.com/sequence-0fb8d9e6-api_docs/gRs22HSdcU_q6TQy/images/guides/analytics/dune_create_query.png?fit=max&auto=format&n=gRs22HSdcU_q6TQy&q=85&s=75dac61fae7896c49227a81030683005" alt="dune create query" width="1985" height="553" data-path="images/guides/analytics/dune_create_query.png" />
    </Frame>
  </Step>

  <Step title="Daily Active Users Query">
    Input the following SQL query in the console, and select `Run`:

    ```sql theme={null}
    SELECT
      t.label as "Date", -- converting the label to "Date"
      t.value as "Count" -- converting the value field to "Count"
    FROM UNNEST(
      TRY_CAST(
        JSON_PARSE(HTTP_GET('https://<URL>/dailyActiveUsers')) AS ARRAY(ROW(label VARCHAR, value DOUBLE))
      )
    ) AS t(label, value)

    ```

    Once the results have been returned, create a `New visualization`.

    Then, select `Add visualization` after the default `Bar chart` is selected from the dropdown (but feel free to customize this).

    <Frame>
      <img src="https://mintcdn.com/sequence-0fb8d9e6-api_docs/gRs22HSdcU_q6TQy/images/guides/analytics/dune_add_visualization.png?fit=max&auto=format&n=gRs22HSdcU_q6TQy&q=85&s=189c9a398d999c2cc076389de087cf1f" alt="add visualization" width="1795" height="749" data-path="images/guides/analytics/dune_add_visualization.png" />
    </Frame>

    Finally, click `Save` and add a name to your query.

    <Frame>
      <img src="https://mintcdn.com/sequence-0fb8d9e6-api_docs/gRs22HSdcU_q6TQy/images/guides/analytics/dune_query_save.png?fit=max&auto=format&n=gRs22HSdcU_q6TQy&q=85&s=5953a2ec2595a59f1b2bdbe7e99d7775" alt="save query" width="1873" height="648" data-path="images/guides/analytics/dune_query_save.png" />
    </Frame>
  </Step>

  <Step title="Total Transactions Sent Query">
    Repeat the steps from the [previous step](/guides/analytics-guide#create-query) and use the following SQL query:

    ```sql theme={null}
    SELECT
      t.label,
      t.value
    FROM UNNEST(
      TRY_CAST(
        JSON_PARSE(HTTP_GET('https://<URL>/totalTransactionsSent')) AS ARRAY(ROW(label VARCHAR, value DOUBLE))
      )
    ) AS t(label, value)

    ```

    Once the results have been returned, create a `New visualization`.

    Then, select `Add visualization` and scroll down to `Counter` to create a counter widget that returns the absolute total returned from the API.
  </Step>

  <Step title="Create New Dashboard">
    Access the buttons that read `Create` > `New dashboard` and input a name for the new dashboard.

    <Frame>
      <img src="https://mintcdn.com/sequence-0fb8d9e6-api_docs/gRs22HSdcU_q6TQy/images/guides/analytics/dune_create_dashboard.png?fit=max&auto=format&n=gRs22HSdcU_q6TQy&q=85&s=9ae5b200dda885ee886d6e80e4e0afcf" alt="dune create dashboard" width="1610" height="450" data-path="images/guides/analytics/dune_create_dashboard.png" />
    </Frame>

    Once created add the previous 2 queries by selecting `Edit` and `Add visualization`.

    <Frame>
      <img src="https://mintcdn.com/sequence-0fb8d9e6-api_docs/gRs22HSdcU_q6TQy/images/guides/analytics/dune_edit_dashboard.png?fit=max&auto=format&n=gRs22HSdcU_q6TQy&q=85&s=62dc01a610c1a7c5749ffb89559c6769" alt="dune edit dashboard" width="1636" height="552" data-path="images/guides/analytics/dune_edit_dashboard.png" />
    </Frame>

    <Frame>
      <img src="https://mintcdn.com/sequence-0fb8d9e6-api_docs/gRs22HSdcU_q6TQy/images/guides/analytics/dune_add_visualization_from_dashboard.png?fit=max&auto=format&n=gRs22HSdcU_q6TQy&q=85&s=282cc14217bc3d4f71a9ed2c28eec011" alt="dune add visualization from dashboard" width="1432" height="554" data-path="images/guides/analytics/dune_add_visualization_from_dashboard.png" />
    </Frame>

    Once for each query, search for the name in the modal and select `Add` for each query, then `Done` in the modal, and `Done` in the dashboard.
  </Step>
</Steps>

Congratulations, you're now ready to share your project's data usage with your teammates or community. Finish by clicking the `Share` button.

<Frame>
  <img src="https://mintcdn.com/sequence-0fb8d9e6-api_docs/gRs22HSdcU_q6TQy/images/guides/analytics/dune_share_dashboard.png?fit=max&auto=format&n=gRs22HSdcU_q6TQy&q=85&s=fcfe3cc4ebdbd3f6c3529a45dbaa528a" alt="dune share dashboard" width="1616" height="683" data-path="images/guides/analytics/dune_share_dashboard.png" />
</Frame>
